/*
MIT License

Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo
*/

#include "simodo/interpret/Interpret.h"
#include "simodo/interpret/AnalyzeException.h"

#include "simodo/loom/Loom_interface.h"
#include "simodo/bormental/DrBormental.h"
#include "simodo/inout/convert/functions.h"

#include <algorithm>
#include <cassert>

#define SemanticFiber_DEBUG_no

namespace simodo::interpret
{
    bool Interpret::wait(const variable::Variable & var)
    {
        const ChildFlowInfo * info = findChildFlowInfo(var);
        if (!info)
            return false;

        if (var.origin().type() != variable::ValueType::Object)
            return false;

        _waiting_for = info->child->fiber();

        return true;
    }

    bool Interpret::push(const variable::Variable & var)
    {
        const ChildFlowInfo * info = findChildFlowInfo(var);
        if (!info)
            return false;

        if (var.origin().type() != variable::ValueType::Object)
            return false;

        std::shared_ptr<variable::Object> source_object = var.origin().value().getObject();
        auto [begin,end] = info->child->stack().boundaryLowAndTop(info->child_bound);

        for(name_index_t i=begin; i < end; ++i) {
            variable::Variable & target_variable = info->child->stack().variable(i);
            if (!target_variable.value().isFunction()) {
                const variable::Variable & source_element = source_object->getVariableByName(target_variable.name());
                assert(!source_element.name().empty());
                target_variable.value() = source_element.value().copy();
            }
        }

        return true;
    }

    bool Interpret::pull(variable::Variable & var)
    {
        const ChildFlowInfo * info = findChildFlowInfo(var);
        if (!info)
            return false;

        if (var.origin().type() != variable::ValueType::Object)
            return false;

        // _loom.wait(info->child->fiber());

        std::shared_ptr<variable::Object> target_object = var.origin().value().getObject();
        auto [begin,end] = info->child->stack().boundaryLowAndTop(info->child_bound);

        for(name_index_t i=begin; i < end; ++i) {
            const variable::Variable & source_element = info->child->stack().variable(i);
            if (!source_element.value().isFunction()) {
                variable::Variable & target_variable = const_cast<variable::Variable &>(target_object->getVariableByName(source_element.name()));
                assert(!target_variable.name().empty());
                target_variable.value() = source_element.value().copy();
            }
        }

        return true;
    }

    // bool Interpret::cut()
    // {
    //     _loom.cut(this);
    //     return true;
    // }

    bool Interpret::addChildFiber(const variable::Variable & var)
    {
        const ChildFlowInfo * info = findChildFlowInfo(var);
        if (info)
            return false;

        if (var.origin().type() != variable::ValueType::Object)
            return false;

        std::shared_ptr<variable::Object> obj = var.origin().value().getObject();

        Interpret * child = new Interpret(_type, _m, _loom, _semantic_factories);

        boundary_index_t child_bound = child->copyBaseBounds(stack());

        child->stack().fillFromObject(obj);
        child->stack().startBoundary();

        /// \todo Это одноразовое копирование не годится для общего случая, особенно для итеративной отладки!
        /// >>>
        child->_files = _files;
        child->_breakpoints = _breakpoints;
        /// <<<

        const variable::Value & name_value = obj->find(u"name");
        std::string name;
        if (name_value.isString())
            name = inout::toU8(name_value.getString());
        else
            name = inout::toU8(var.origin().name());

        if (!_loom.dock(child,true,this,loom::FiberStatus::Delayed,name)) {
            delete child;
            return false;
        }

        _childs.push_back({obj,child,child_bound});
        return true;
    }

    const ChildFlowInfo * Interpret::findChildFlowInfo(const variable::Variable & var) const
    {
        if (var.origin().type() != variable::ValueType::Object)
            return nullptr;

        std::shared_ptr<variable::Object> obj = var.origin().value().getObject();

        auto it = std::find_if( _childs.begin(), _childs.end(),
                                [obj](const ChildFlowInfo & info)
                                {
                                    return info.our_object.get() == obj.get();
                                });

        if (it == _childs.end())
            return nullptr;

        return it.base();
    }


}